import React from 'react';
import classNames from 'classnames';
import Icon from '../Icon';
import DatePciker from '../DateTime/Date';
import Button from '../Button';
import Time from '../Time/Time';
import {dateFormat, parseDate, alignPostion} from '../utils/Utils';
import '../DateTime/DateTime.less';
import './DateRange.less';
import Dom from '../utils/Dom';

class DateRange extends React.Component {
    static displayName = 'DateRange';

    static defaultProps = {
        format: 'yyyy-MM-dd'
    }

    constructor (props) {
        super(props);
        let current = new Date();
        const value = props.value;
        if (value) {
            current = this.getvalueDates()[0];
        }
        
        this.state = {
            current,
            open: false,
            startView: 'date',
            endView: 'date',
            view: 'date',
            selected: [],
            optionStyle: {},
            overDate: null
        };
    }

    hoverTimer = null;

    onPrevYear = (current) => {
        this._isMounted && this.setState({
            current
        });
    }
    onEndPrevYear = (current) => {
        current.setMonth(current.getMonth() - 1);
        this._isMounted && this.setState({
            current
        });
    }

    onNextYear = (current) => {
        this._isMounted && this.setState({
            current
        });
    }

    onEndNextYear = (current) => {
        current.setMonth(current.getMonth() - 1);
        this._isMounted && this.setState({
            current
        });
    }

    onPrevMonth = (current) => {
        this._isMounted && this.setState({
            current
        });
    }

    onNextMonth = (current) => {
        this._isMounted && this.setState({
            current
        });
    }

    onEndPrevMonth = (current) => {
        current.setMonth(current.getMonth() - 1);
        this._isMounted && this.setState({
            current
        });
    }

    onEndNextMonth = (current) => {
        current.setMonth(current.getMonth() - 1);
        this._isMounted && this.setState({
            current
        });
    }

    onStartChange = (date) => {
        this.onRangeChange(date);
    }

    onEndChange = (date) => {
        this.onRangeChange(date);
    }

    onRangeChange (date) {
        const {maxRange} = this.props;
        let {selected} = this.state;
        if (selected.length === 2 || selected.length === 0) {
            selected = [date];
        } else {
            const ms = selected[0].getTime() - date.getTime();
            const days = Math.abs(ms / 1000 / 60 / 60 / 24);
            if (maxRange) {
                if (days > maxRange - 1) {
                    return false;
                }
            }
            selected.push(date);
        }
        if (selected.length === 2) {
            selected.sort((a, b) => {
                return a.getTime() - b.getTime();
            });
        }

        this.setState({
            selected,
            overDate: selected.length === 1 ? selected[0] : this.state.overDate
        }, () => {
            if (selected.length === 2) {
                this.props.onChange && this.props.onChange(selected);
                if (this.props.type !== 'datetimerange') {
                    window.setTimeout(() => {
                        this.close();
                    }, 200);
                }
            }
        });
    }

    onChangeStartMonth = (date) => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                current: date,
                startView: 'date'
            });
        });
    }

    onChangeStartYear = (date) => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                current: date,
                startView: 'month'
            });
        });
    }

    onChangeEndMonth = (date) => {
        const current = new Date(date);
        current.setMonth(current.getMonth() - 1);
        window.setTimeout(() => {
            this._isMounted && this.setState({
                current,
                endView: 'date'
            });
        });
    }

    onChangeEndYear = (date) => {
        const current = new Date(date);
        current.setMonth(current.getMonth() - 1);
        window.setTimeout(() => {
            this._isMounted && this.setState({
                current,
                endView: 'month'
            });
        });
    }

    componentDidMount () {
        this._isMounted = true;
        document.addEventListener('click', this.clickAnyWhere.bind(this), false);
    }

    componentWillUnmount () {
        this._isMounted = false;
        document.removeEventListener('click', this.clickAnyWhere);
    }

    clickAnyWhere (e) {
        const ele = e.target || e.srcElement;
        const parent = Dom.closest(ele, '.cm-datetime');
        if (parent && parent == this.wrap) {
            return ;
        } else {
            this._isMounted && this.setState({
                open: false
            });
        }
    }

    clearHms (date) {
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
    }

    onShowHide = (e) => {
        const {disabled, value, align, format} = this.props;
        const ele = e.target || e.srcElement;
        // click clear icon
        if (ele.tagName === 'I' && ele.className.indexOf('cm-datetime-clear') !== -1) {
            return;
        }
        if (disabled) {
            return;
        }

        const open = this.state.open;
        let selected = this.state.selected;
        if (!open) {
            const valueDates = this.getvalueDates();
            const startValue = valueDates[0];
            startValue && this.clearHms(startValue);
            const endValue = valueDates[1];
            endValue && this.clearHms(endValue);
            selected = [startValue, endValue];
        }

        this._isMounted && this.setState({
            open: !open,
            selected,
            startView: 'date',
            endView: 'date'
        }, () => {
            const optionStyle = alignPostion(align, this.rel, this.dropdown);
            this.setState({
                optionStyle
            });
        });
    }

    close () {
        this._isMounted && this.setState({
            open: false
        });
    }

    getDefaultView () {
        const {type} = this.props;
        switch (type) {
            case 'date' : {
                return 'date';
            }
            case 'year' : {
                return 'year';
            }
            case 'month' : {
                return 'month';
            }
            default : {
                return 'date';
            }
        }
    }

    onStartGotoMonthView = () => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                startView: 'month'
            });
        });
    }
    onEndGotoMonthView = () => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                endView: 'month'
            });
        });
    }

    onStartGotoYearView = () => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                startView: 'year'
            });
        });
    }

    onEndGotoYearView = () => {
        window.setTimeout(() => {
            this._isMounted && this.setState({
                endView: 'year'
            });
        });
    }

    onHoverDate = (date) => {
        const {maxRange} = this.props;
        window.clearTimeout(this.hoverTimer);
        this.hoverTimer = window.setTimeout(() => {
            window.clearTimeout(this.hoverTimer);
            if (this.state.selected.length === 1 && this._isMounted && date) {
                const ms = this.state.selected[0].getTime() - date.getTime();
                const days = Math.abs(ms / 1000 / 60 / 60 / 24);
                // has maxRange prop
                if (maxRange) {
                    // in range
                    if (days > maxRange - 1) {
                        date = new Date(this.state.selected[0]);
                        date.setDate(date.getDate() + (maxRange - 1) * (ms < 0 ? 1 : -1));
                    }
                    this.setState({
                        overDate: date
                    });
                } else {
                    this.setState({
                        overDate: date
                    });
                }
            }
        });
    }

    onClear = () => {
        this.props.onChange && this.props.onChange('');
    }

    onShortCutClick = (item) => {
        if (item.value && typeof item.value === 'function') {
            this.props.onChange && this.props.onChange(item.value());
            if (item.onClick) {
                item.onClick();
            }
            this.close();
        }
    }

    renderShortCuts = () => {
        return this.props.shortcuts.map((item, index) => {
            return <div className='cm-datetime-shortcut-item' key={index} onClick={this.onShortCutClick.bind(this, item)}>{item.text}</div>;
        });
    }

    toggleTimeAndDate = () => {
        const view = this.state.view === 'date' ? 'time' : 'date';
        this._isMounted && this.setState({
            view
        });
    }

    getTimeFormat () {
        const format = this.props.format;
        if (/(h+).*(m+).*(s+).*/.test(format)) {
            return format.match(/(h+).*(m+).*(s+)/)[0];
        }
        if (/(h+).*(m+).*/.test(format)) {
            return format.match(/(h+).*(m+)/)[0];
        }
        if (/(h+).*/.test(format)) {
            return format.match(/(h+).*/)[0];
        }
        return null;
    }

    getTimeCellsNum (format) {
        let num = 0;
        if (format.indexOf('h') !== -1) {
            num++;
        }
        if (format.indexOf('m') !== -1) {
            num++;
        }
        if (format.indexOf('s') !== -1) {
            num++;
        }
        return num;
    }

    getvalueDates () {
        let {format, value} = this.props;
        let startDate;
        let endDate;
        if (value) {
            if (value instanceof Array) {
                // to do
            } else {
                value = value.split('~');
            }
            startDate = typeof value[0] === 'string' ? parseDate(format, value[0]) : value[0];
            endDate = typeof value[1] === 'string' ? parseDate(format, value[1]) : value[1];
        }
        return [startDate, endDate];
    }

    onChangeStartTime = (current) => {
        const selected = this.state.selected;
        selected[0].setHours(current[0]);
        selected[0].setMinutes(current[1]);
        selected[0].setSeconds(current[2]);
        const endDate = this.getvalueDates()[1];
        selected[1].setHours(endDate.getHours());
        selected[1].setMinutes(endDate.getMinutes());
        selected[1].setSeconds(endDate.getSeconds());
        this.sortTime(selected);
        this.props.onChange && this.props.onChange(selected);
    }

    onChangeEndTime = (current) => {
        const selected = this.state.selected;
        const times = [new Date(selected[0]), new Date(selected[1])];
        times[1].setHours(current[0]);
        times[1].setMinutes(current[1]);
        times[1].setSeconds(current[2]);
        if (this.isEndBefore(times)) {
            return false;
        }
        const startDate = this.getvalueDates()[0];
        selected[0].setHours(startDate.getHours());
        selected[0].setMinutes(startDate.getMinutes());
        selected[0].setSeconds(startDate.getSeconds());
        selected[1].setHours(current[0]);
        selected[1].setMinutes(current[1]);
        selected[1].setSeconds(current[2]);
        this.props.onChange && this.props.onChange(selected);
    }

    isEndBefore (time) {
        if (dateFormat('yyyyMMdd', time[0]) === dateFormat('yyyyMMdd', time[1])) {
            if (time[0].getHours() >= time[1].getHours()) {
                return true;
            } else {
                if (time[0].getHours() === time[1].getHours()
            && time[0].getMinutes() >= time[1].getMinutes()) {
                    return true;
                } else {
                    if (time[0].getHours() === time[1].getHours()
                && time[0].getMinutes() === time[1].getMinutes()
                && time[0].getSeconds() >= time[1].getSeconds()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    sortTime (time) {
        if (dateFormat('yyyyMMdd', time[0]) === dateFormat('yyyyMMdd', time[1])) {
            if (time[0].getHours() > time[1].getHours()) {
                time[1].setHours(time[0].getHours());
            } else {
                if (time[0].getMinutes() > time[1].getMinutes()) {
                    time[1].setMinutes(time[0].getMinutes());
                } else {
                    if (time[0].getSeconds() > time[1].getSeconds()) {
                        time[1].setSeconds(time[0].getSeconds());
                    }
                }
            }
        }
    }

    render () {
        let {className, style, theme, disabled, align, name, value, clearable, shortcuts, type,
            placeholder, size, disabledDate, disabledPrev, disabledNext} = this.props;
        const timeFormat = type === 'datetimerange' ? this.getTimeFormat() : undefined;
        const num = type === 'datetimerange' ? this.getTimeCellsNum(timeFormat) : 0;
        const clazzName = classNames(className, 'cm-datetime', 'cm-daterange', {
            'cm-datetime-disabled': disabled,
            [`cm-datetime-${align}`]: align,
            [`cm-datetime-${theme}`]: theme,
            [`cm-datetime-${size}`]: size,
            'cm-datetime-clearable': !disabled && clearable && value,
            'cm-datetime-open': this.state.open,
            'cm-datetime-with-range-one': num === 1,
            'cm-datetime-with-range-two': num === 2,
            'cm-datetime-with-range-three': num === 3,
            'cm-datetime-has-shortcuts': shortcuts
        });
        const format = this.props.format;
        
        let startValue = null;
        let endValue = null;
        if (value) {
            if (value instanceof Array) {
                // to do
            } else {
                value = value.split('~');
            }
            startValue = value[0];
            endValue = value[1];
        }
        const startCurrent = this.state.current;
        let endCurrent = undefined;
        if (startCurrent) {
            endCurrent = new Date(startCurrent);
            endCurrent.setMonth(endCurrent.getMonth() + 1);
        }

        const valueDates = this.getvalueDates();
        const startDate = valueDates[0];
        const endDate = valueDates[1];
        
        const current1 = startValue ? [startDate.getHours(), startDate.getMinutes(), startDate.getSeconds()] : [0, 0, 0];
        const current2 = endValue ? [endDate.getHours(), endDate.getMinutes(), endDate.getSeconds()] : [0, 0, 0];

        const text = value ? `${dateFormat(format, startDate)}~${dateFormat(format, endDate)}` : (placeholder ? placeholder : '');
        return <div className={clazzName} style={style} ref={f => this.wrap = f}>
            <div className='cm-datetime-value' onClick={this.onShowHide} tabIndex='0' ref={f => this.rel = f}>
                <input type='hidden' name={name} value={text} />
                <div className={`cm-datetime-text ${!value && placeholder ? 'cm-datetime-text-placeholder' : ''}`}>{text}</div>
                <Icon name='calendar' className='cm-datetime-cert'/>
                {
                    clearable
                        ? <Icon name='x-circle' className='cm-datetime-clear' onClick={this.onClear}/>
                        : null
                }
            </div>
            <div className='cm-datetime-wrap' ref={f => this.dropdown = f} style={this.state.optionStyle}>
                {
                    shortcuts
                        ? <div className='cm-datetime-shortcuts'>
                            {this.renderShortCuts()}
                        </div>
                        : null
                }
                {
                    this.state.view === 'time'
                        ? null
                        : <div style={{float: 'left'}} className='cm-datetime-group'>
                            <DatePciker type='date' view={this.state.startView} current={startCurrent} 
                                onPrevYear={this.onPrevYear} onNextYear={this.onNextYear} onPrevMonth={this.onPrevMonth} 
                                onSelectMonth={this.onChangeStartMonth} onSelectYear={this.onChangeStartYear} gotoYearView={this.onStartGotoYearView}
                                onNextMonth={this.onNextMonth} onChange={this.onStartChange} gotoMonthView={this.onStartGotoMonthView}
                                disabledDate={disabledDate} selected={this.state.selected} disabledPrev={disabledPrev} disabledNext={disabledNext}
                                onHoverDate={this.onHoverDate} overDate={this.state.overDate}/>
                            <DatePciker type='date' view={this.state.endView} current={endCurrent} 
                                onPrevYear={this.onEndPrevYear} onNextYear={this.onEndNextYear} onPrevMonth={this.onEndPrevMonth} 
                                onSelectMonth={this.onChangeEndMonth} onSelectYear={this.onChangeEndYear} gotoYearView={this.onEndGotoYearView}
                                onNextMonth={this.onEndNextMonth} onChange={this.onEndChange} gotoMonthView={this.onEndGotoMonthView}
                                disabledDate={disabledDate} selected={this.state.selected} disabledPrev={disabledPrev} disabledNext={disabledNext}
                                onHoverDate={this.onHoverDate} overDate={this.state.overDate}/>
                        </div>
                }
                {
                    type === 'datetimerange' && this.state.view === 'time'
                        ? <div>
                            <div className='cm-time-range-item'>
                                <div className='cm-time-range-title'>开始时间</div>
                                <div className='cm-time-range-body'>
                                    <Time format={timeFormat} current={current1} onChange={this.onChangeStartTime} />
                                </div>
                            </div>
                            <div className='cm-time-range-item'>
                                <div className='cm-time-range-title'>结束时间</div>
                                <div className='cm-time-range-body'>
                                    <Time format={timeFormat} current={current2} onChange={this.onChangeEndTime} />
                                </div>
                            </div>
                        </div>
                        : null
                }
                {
                    type === 'datetimerange'
                        ? <div className='cm-datetime-tools'>
                            <Button size='small' theme='text' disabled={!value} onClick={this.toggleTimeAndDate}>{
                                this.state.view === 'time' ? '选择日期' : '选择时间'
                            }</Button>
                        </div>
                        : null
                }
            </div>
        </div>;
    }
}

export default DateRange;
